package com.zeng.sboot.oss.bucket;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

import static com.zeng.sboot.oss.common.AccessKey.accessKeyId;
import static com.zeng.sboot.oss.common.AccessKey.accessKeySecret;

/**
 *  操作 版本控制 的 Bucket 存储空间
 *
 *  1 . 设置 bucket 开通版本控制
 *  2 . 获取 bucket 版本状态
 *  3 . 列举 bucket 所有 object 的所有版本
 *
 *  21.
 *
 */
public class VersionBucketDemo {

    private String endpoint = "https://oss-cn-beijing.aliyuncs.com";
    private String delBucketName = "fapd2018";

    /**
     * 【 设置Bucket版本控制状态 】
     */
    // 设置 Bucket 为开启版本控制（Enabled）或暂停版本控制（Suspended）状态
    public void test1() {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 设置存储空间版本控制状态为Enabled。
        BucketVersioningConfiguration configuration = new BucketVersioningConfiguration();
        configuration.setStatus(BucketVersioningConfiguration.ENABLED);
        SetBucketVersioningRequest request = new SetBucketVersioningRequest(delBucketName, configuration);
        ossClient.setBucketVersioning(request);

        // 关闭OSSClient。
        ossClient.shutdown();

    }

    /**
     * 【 获取Bucket版本控制状态信息 】
     */
    public void test2() {

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 获取存储空间版本控制状态信息。
        BucketVersioningConfiguration versionConfiguration = ossClient.getBucketVersioning(delBucketName);
        System.out.println("bucket versioning status: " + versionConfiguration.getStatus());

        // 关闭OSSClient。
        ossClient.shutdown();
    }



    /**
     * 【 列举Bucket中所有Object的版本信息 】
     */
    //列举指定Bucket中所有Object的版本信息，包括删除标记（Delete Marker）的object
    public void test3() {

        // 创建OSSClient实例。https://oss-cn-shanghai.aliyuncs.com
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 列举所有的Object的版本信息。
        String nextKeyMarker = null;
        String nextVersionMarker = null;
        VersionListing versionListing = null;
        do {
            ListVersionsRequest listVersionsRequest = new ListVersionsRequest()
                    .withBucketName(delBucketName)
                    .withKeyMarker(nextKeyMarker)
                    .withVersionIdMarker(nextVersionMarker);

            versionListing = ossClient.listVersions(listVersionsRequest);
            for (OSSVersionSummary ossVersion : versionListing.getVersionSummaries()) {
                System.out.println("key name: " + ossVersion.getKey());
                System.out.println("versionid: " + ossVersion.getVersionId());
                System.out.println("Is latest: " + ossVersion.isLatest());
                System.out.println("Is delete marker: " + ossVersion.isDeleteMarker());
            }

            nextKeyMarker = versionListing.getNextKeyMarker();
            nextVersionMarker = versionListing.getNextVersionIdMarker();
        } while (versionListing.isTruncated());

        // 关闭OSSClient。
        ossClient.shutdown();
    }

    /**
     * 【 查询 版本控制 的 Bucket 中指定 object 的所有版本 】
     */
    public void test4() {
        // 创建OSSClient实例。https://oss-cn-shanghai.aliyuncs.com
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 列举所有的Object的版本信息。

        ListObjectsRequest listObjectsRequest = new ListObjectsRequest(delBucketName);
        listObjectsRequest.setPrefix("test2/");
        //listObjectsRequest.withKey("test3/lucene.avi");


        //ObjectListing objectListing = ossClient.listObjects(delBucketName, "test2/");
        ObjectListing objectListing = ossClient.listObjects(listObjectsRequest);
        List<OSSObjectSummary> objectSummaries = objectListing.getObjectSummaries();

        for (OSSObjectSummary s : objectSummaries ) {
            System.out.println("getETag: " + s.getETag());
            System.out.println("getKey: " + s.getKey());
            System.out.println("getLastModified: " + s.getLastModified());
            System.out.println("getStorageClass: " + s.getStorageClass());
            System.out.println("getOwner(): " + s.getOwner());
            System.out.println("getSize(): " + s.getSize());
        }

//        getETag: F0F18C2C66AE1DD512BDCD4366F76DA3
//        getKey: test/text,txt
//        getLastModified: Thu Jun 04 10:57:21 CST 2020
//        getStorageClass: Archive
//        getOwner(): Owner [name=1790385276695855,id=1790385276695855]
//        getSize(): 9
//        getETag: F0F18C2C66AE1DD512BDCD4366F76DA3
//        getKey: test/text2.txt
//        getLastModified: Thu Jun 04 10:58:13 CST 2020
//        getStorageClass: Archive
//        getOwner(): Owner [name=1790385276695855,id=1790385276695855]
//        getSize(): 9
//        getETag: F0F18C2C66AE1DD512BDCD4366F76DA3
//        getKey: test2/text2.txt
//        getLastModified: Thu Jun 04 11:00:21 CST 2020
//        getStorageClass: Archive
//        getOwner(): Owner [name=1790385276695855,id=1790385276695855]
//        getSize(): 9

        // 关闭OSSClient。
        ossClient.shutdown();
    }

    /**
     * 通过 控制 台来操作显示某个文件的版本
     */
    public void test5() {
        //ossutil64 ls oss://fapd2018/test3 --all-versions -e oss-cn-beijing.aliyuncs.com -i LTAI4Fgv31EEiYsFYFfrZZ3g -k 8gs7sPvcgZkIbUXqjjfeDSwrFUNiOE

        try {
            Process process = Runtime.getRuntime().exec("C:\\Users\\zengzhijun\\Desktop\\aliyun-oss\\ossutil64\\ossutil64 ls oss://fapd2018/test3 --all-versions -e oss-cn-beijing.aliyuncs.com -i LTAI4Fgv31EEiYsFYFfrZZ3g -k 8gs7sPvcgZkIbUXqjjfeDSwrFUNiOE");
            InputStream is = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is, "gbk"));
            String rc = null;
            while ((rc = reader.readLine()) != null) {
                System.out.println(rc);
            }
            is.close();
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 【 简单上传 】
     */
    // 在已开启版本控制的Bucket中，OSS会为新添加的Object自动生成唯一的版本ID，并在响应header中通过x-oss-version-id形式返回。
    // 在暂停了版本控制的Bucket中，新添加的Object的版本ID为“null”，上传同名Object，后一次会覆盖前一次上传的文件内容。
    // OSS保证同一个Object只会有一个版本的ID为“null”。
    public void test21() {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 以上传字符串为例。
        String content = "Hello OSS222222";
        PutObjectResult result = ossClient.putObject(delBucketName, "test2/text2.txt", new ByteArrayInputStream(content.getBytes()));
        // 查看此次上传object的版本id。
        System.out.println("result.versionid: " + result.getVersionId());

        // 关闭OSSClient。
        ossClient.shutdown();
    }

    /**
     * 【 追加上传 】
     */
    // 在受版本控制的Bucket中，仅支持对于当前版本为Appendable类型的Object执行追加（AppendObject）操作，
    // 不支持对于历史版本为Appendable类型的Object执行AppendObject操作
    // -> 对当前版本为Appendable类型的Object执行AppendObject操作时，OSS不会为该Appendable类型的Object生成历史版本
    // -> 对当前版本为Appendable类型的Object执行PutObject或DeleteObject操作时，OSS会将该Appendable类型的Object保留为历史版本，且该Object不允许继续追加。
    // -> 不支持对当前版本为非Appendable类型的Object（包括 Normal Object、Delete Marker等）执行AppendObject 操作。
    // -> 目标Bucket在开启或暂停版本控制状态下，不支持对Appendable类型Object执行拷贝操作。
    public void test22() {

        /**
         *  如果文件不支持 append 会报 Exception in thread "main" com.aliyun.oss.OSSException: The object is not appendable
         */

        String content1 = "Hello OSS A \n";
        String content2 = "Hello OSS B \n";
        String content3 = "Hello OSS C \n";

        String objectName = "test2/text2.txt";


        // 创建OSSClient实例。
        OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);

        ObjectMetadata meta = new ObjectMetadata();
        // 指定上传的内容类型。
        meta.setContentType("text/plain");

        // 通过AppendObjectRequest设置多个参数。
        AppendObjectRequest appendObjectRequest = new AppendObjectRequest(delBucketName, objectName, new ByteArrayInputStream(content1.getBytes()),meta);

        // 通过AppendObjectRequest设置单个参数。
        // 设置存储空间名称。
        //appendObjectRequest.setBucketName("<yourBucketName>");
        // 设置文件名称。
        //appendObjectRequest.setKey("<yourObjectName>");
        // 设置待追加的内容。有两种可选类型：InputStream类型和File类型。这里为InputStream类型。
        //appendObjectRequest.setInputStream(new ByteArrayInputStream(content1.getBytes()));
        // 设置待追加的内容。有两种可选类型：InputStream类型和File类型。这里为File类型。
        //appendObjectRequest.setFile(new File("<yourLocalFile>"));
        // 指定文件的元信息，第一次追加时有效。
        //appendObjectRequest.setMetadata(meta);

        // 第一次追加。
        // 设置文件的追加位置。
        appendObjectRequest.setPosition(0L);
        AppendObjectResult appendObjectResult = ossClient.appendObject(appendObjectRequest);
        // 文件的64位CRC值。此值根据ECMA-182标准计算得出。
        System.out.println(appendObjectResult.getObjectCRC());

        // 第二次追加。
        // nextPosition指明下一次请求中应当提供的Position，即文件当前的长度。
        appendObjectRequest.setPosition(appendObjectResult.getNextPosition());
        appendObjectRequest.setInputStream(new ByteArrayInputStream(content2.getBytes()));
        appendObjectResult = ossClient.appendObject(appendObjectRequest);

        // 第三次追加。
        appendObjectRequest.setPosition(appendObjectResult.getNextPosition());
        appendObjectRequest.setInputStream(new ByteArrayInputStream(content3.getBytes()));
        appendObjectResult = ossClient.appendObject(appendObjectRequest);

        // 关闭OSSClient。
        ossClient.shutdown();

    }

    /**
     * 【 分片上传 】
     */
    // 在受版本控制的Bucket中，调用CompleteMultipartUpload接口来完成整个文件的分片上传，OSS会为整个文件生成唯一的版本ID，并在响应header中以x-oss-version-id的形式返回。
    public void test23() throws IOException {

        String localFile = "E:\\lucene.avi";
        String objectName = "test3/lucene.avi";

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        /* 步骤1：初始化一个分片上传事件。
         */
        InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(delBucketName, objectName);
        InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
        // 返回uploadId，它是分片上传事件的唯一标识，您可以根据这个ID来发起相关的操作，如取消分片上传、查询分片上传等。
        String uploadId = upresult.getUploadId();

        /* 步骤2：上传分片。
         */
        // partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
        List<PartETag> partETags =  new ArrayList<PartETag>();
        // 计算文件有多少个分片。
        final long partSize = 1 * 1024 * 1024L;   // 1MB
        final File sampleFile = new File(localFile);
        long fileLength = sampleFile.length();
        int partCount = (int) (fileLength / partSize);
        if (fileLength % partSize != 0) {
            partCount++;
        }
        // 遍历分片上传。
        for (int i = 0; i < partCount; i++) {
            long startPos = i * partSize;
            long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
            InputStream instream = new FileInputStream(sampleFile);
            // 跳过已经上传的分片。
            instream.skip(startPos);
            UploadPartRequest uploadPartRequest = new UploadPartRequest();
            uploadPartRequest.setBucketName(delBucketName);
            uploadPartRequest.setKey(objectName);
            uploadPartRequest.setUploadId(uploadId);
            uploadPartRequest.setInputStream(instream);
            // 设置分片大小。除了最后一个分片没有大小限制，其他的分片最小为100KB。
            uploadPartRequest.setPartSize(curPartSize);
            // 设置分片号。每一个上传的分片都有一个分片号，取值范围是1~10000，如果超出这个范围，OSS将返回InvalidArgument的错误码。
            uploadPartRequest.setPartNumber( i + 1);
            // 每个分片不需要按顺序上传，甚至可以在不同客户端上传，OSS会按照分片号排序组成完整的文件。
            UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
            // 每次上传分片之后，OSS的返回结果会包含一个PartETag。PartETag将被保存到partETags中。
            partETags.add(uploadPartResult.getPartETag());
        }

        /* 步骤3：完成分片上传。
         */
        // 在执行该操作时，需要提供所有有效的partETags。OSS收到提交的partETags后，会逐一验证每个分片的有效性。当所有的数据分片验证通过后，OSS将把这些分片组合成一个完整的文件。
        CompleteMultipartUploadRequest completeMultipartUploadRequest =
                new CompleteMultipartUploadRequest(delBucketName, objectName, uploadId, partETags);

        CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest);
        // 查看上传文件的版本id。
        System.out.println("restore object versionid: " + completeMultipartUploadResult.getVersionId());

        // 关闭OSSClient。
        ossClient.shutdown();
    }


    /**
     * 【 下载文件 】
     */
    // 默认情况下，在受版本控制的存储空间（Bucket）中调用GetObject接口仅返回文件（Object）的当前版本。
    // 对某个Bucket执行GetObject操作时，分以下三种情况:
    // --> 如果该Bucket中Object的当前版本是删除标记（Delete Marker），则返回404 Not Found。
    // --> 如果在查询参数中指定Object的versionId，则返回指定的Object版本。当versionId指定为“null”时，则返回versionId为“null”的Object版本。
    // --> 通过指定versionId的方式来获取删除标记时，则返回405 Method Not Allowed。
    public void test41() throws IOException {

        String objectName="test2/text2.txt";


        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 封装GetObject请求。
        GetObjectRequest getObjectRequest = new GetObjectRequest(delBucketName, objectName);
        // 指定object版本id。
        //getObjectRequest.setVersionId("CAEQBBiBgIDroMvkkxciIDIxNDQ2NmY2ZGQ3ZDRlNjg4M2RjMGE2YTU2ZTg0MTFm");//标记 删除文件   -》 405 Method Not Allowed
        getObjectRequest.setVersionId("CAEQBBiBgMC7u4v1kxciIGFlY2M1ZDY1OGE0NzQwYWE5NTVkMDg3YWQ0ODcwMWIz");//存在的文件
        // ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
        OSSObject ossObject = ossClient.getObject(getObjectRequest);

        // 查看下载文件的版本id。
        System.out.println("Get Object versionid:" +  ossObject.getObjectMetadata().getVersionId());
        // 读取文件内容。
        System.out.println("Object content:");
        BufferedReader reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));
        while (true) {
            String line = reader.readLine();
            if (line == null) break;
            System.out.println("\n" + line);
        }

        // 关闭OSSClient。
        ossClient.shutdown();
    }

    /**
     * 【 删除文件 】
     * 【 删除单个文件 】
     * 对于开启版本控制的Bucket执行DeleteObject操作时，如果未指定versionId ，则保留Object的当前版本，并插入删除标记（Delete Marker）。
     * 如果指定versionId，则永久删除该指定版本ID的Object。
     *
     * 【 未指定versionId： 】
     * 如果在未指定versionId的情况下执行DeleteObject操作时，默认仅作用于Object的当前版本，但不会直接删除该Object的当前版本，而是插入一个删除标记（Delete Marker），并保留原来的当前版本。
     * 当执行GetObject操作时，OSS会检测到当前版本为删除标记，并返回404 Not Found。此外，响应中会返回header：x-oss-delete-marker = true以及新生成的删除标记的版本号x-oss-version-id。
     * x-oss-delete-marker的值为true，表示与返回的x-oss-version-id对应的版本为删除标记。
     *
     * 【 指定versionId： 】
     * 如果在指定versionId的情况下调用delete_object接口时，OSS会根据params参数中指定的versionId参数永久删除该版本。
     * 如果要删除ID为“null”的版本，则调用delete_object接口时，在params参数中添加 params['versionId'] = “null”，OSS将“null”字符串当成“null”版本的ID号，从而删除版本ID为“null”的Object。
     */
    // 【永久删除】 指定版本Object
    public void test51() {

        String objectName="test3/lucene.avi";
        //CAEQBBiBgICc54P3kxciIDM0NTAzZjlmZWNkOTRhMGViYWYzN2VlOTY3YThhZThh
        String versionid="CAEQBBiBgICZ0K31kxciIDQyZGRhYzQ5ZGQwZjQzMWM4ZjJjNTRkZGIzM2FkNjlk";

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 删除指定版本的object。
        ossClient.deleteVersion(delBucketName, objectName , "CAEQBBiBgIDdk5b4kxciIGEyNjgwNWM0MTU1MzRjZTBiYWE3M2ZiMjllY2VmNzE0");
        //ossClient.deleteObject(new GenericRequest(delBucketName, objectName, "CAEQBBiBgIDdk5b4kxciIGEyNjgwNWM0MTU1MzRjZTBiYWE3M2ZiMjllY2VmNzE0"));

        // 关闭OSSClient。
        ossClient.shutdown();
    }
    // 【临时删除】 Object
    public void test52() {

        String objectName="test3/lucene.avi";
        String versionid="CAEQBBiBgICZ0K31kxciIDQyZGRhYzQ5ZGQwZjQzMWM4ZjJjNTRkZGIzM2FkNjlk";

        // 创建OSSClient实例。
        OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);

        // 开启版本控制状态下，此方法为临时删除，将会给object添加删除标记。
        ossClient.deleteObject(delBucketName, objectName);

        //13:15:59.632 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "x-oss-delete-marker: true[\r][\n]"       返回true表示标记删除
        //13:15:59.632 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "x-oss-version-id: CAEQBBiBgICc54P3kxciIDM0NTAzZjlmZWNkOTRhMGViYWYzN2VlOTY3YThhZThh[\r][\n]" 标记删除后的新版本ID

        // 关闭OSSClient。
        ossClient.shutdown();
    }

    // 【批量删除】object 多个版本
    public void test53() {
        //ossutil64 rm oss://fapd2018/test3/lucene.avi --all-versions -e oss-cn-beijing.aliyuncs.com -i LTAI4Fgv31EEiYsFYFfrZZ3g -k 8gs7sPvcgZkIbUXqjjfeDSwrFUNiOE
        try {
            // 指定 文件
            //Process process = Runtime.getRuntime().exec("C:\\Users\\zengzhijun\\Desktop\\aliyun-oss\\ossutil64\\ossutil64 rm oss://fapd2018/test3/lucene.avi --all-versions -e oss-cn-beijing.aliyuncs.com -i LTAI4Fgv31EEiYsFYFfrZZ3g -k 8gs7sPvcgZkIbUXqjjfeDSwrFUNiOE");

            // 指定 bucket 或 文件夹下所有
            Process process = Runtime.getRuntime().exec("C:\\Users\\zengzhijun\\Desktop\\aliyun-oss\\ossutil64\\ossutil64 rm oss://fapd2018 -bar -e oss-cn-beijing.aliyuncs.com -i LTAI4Fgv31EEiYsFYFfrZZ3g -k 8gs7sPvcgZkIbUXqjjfeDSwrFUNiOE");

            InputStream is = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is, "gbk"));
            String rc = null;
            while ((rc = reader.readLine()) != null) {
                System.out.println(rc);
            }
            is.close();
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 【 解冻文件 】
     */
    // 在受版本控制的存储空间（Bucket）中，Object的各个版本可以对应不同的存储类型。RestoreObject接口默认解冻Object的当前版本，允许通过指定versionId的方式来解冻指定版本的Object。
    public void test71() throws Exception {
        String objectName="test3/lucene.avi";
        String versionid="CAEQBBiBgICZ0K31kxciIDQyZGRhYzQ5ZGQwZjQzMWM4ZjJjNTRkZGIzM2FkNjlk";

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 获取指定版本的文件的部分元信息。
        GenericRequest getObjectMetadataRequest = new GenericRequest(delBucketName, objectName, versionid);
        ObjectMetadata objectMetadata = ossClient.getObjectMetadata(getObjectMetadataRequest);

        // 校验指定版本的文件是否为归档文件。
        StorageClass storageClass = objectMetadata.getObjectStorageClass();
        System.out.println("object storage class:" + objectMetadata.getObjectStorageClass());
        if (storageClass == StorageClass.Archive) {
            // 解冻文件。
            GenericRequest genericRequest = new GenericRequest(delBucketName, objectName, versionid);
            RestoreObjectResult restoreObjectResult = ossClient.restoreObject(genericRequest);
            System.out.println("restor versionid: " + restoreObjectResult.getVersionId());

            // 等待解冻完成。
            do {
                Thread.sleep(1000);
                objectMetadata = ossClient.getObjectMetadata(getObjectMetadataRequest);
                System.out.println("is competed:" + objectMetadata.isRestoreCompleted());
            } while (!objectMetadata.isRestoreCompleted());
        }

        // 获取指定版本解冻文件。
        GetObjectRequest getObjectRequest = new GetObjectRequest(delBucketName, objectName);
        getObjectRequest.setVersionId(versionid);
        OSSObject ossObject = ossClient.getObject(getObjectRequest);
        ossObject.getObjectContent().close();

        ossClient.shutdown();
    }







    public static void main(String[] args) throws Exception {
        VersionBucketDemo demo = new VersionBucketDemo();

        demo.test53();
        //demo.test5();
    }
}
//    key name: test/
//        versionid: CAEQBBiBgMCczoT1kxciIDRhYjUxNTdjNWIxMDQzNzQ5MmI2Y2E2NGU3MTZmYWZj
//        Is latest: true
//        Is delete marker: false
//        key name: test/
//        versionid: CAEQAhiBgID3vdHCkRciIDM2Njc3MjVjMGI1ZjQ2OTM4ZDA5YWZiMTY0MjAxOGYx
//        Is latest: false
//        Is delete marker: false
//        key name: test/text,txt
//        versionid: CAEQBBiBgMC4.oX1kxciIGY2NWY2OWZhMTFjNjQ4YjViNzczYjEyZTFlZDY0ODNl
//        Is latest: true
//        Is delete marker: false
//        key name: test/text2.txt
//        versionid: CAEQBBiBgIDzxYf1kxciIDMxOTU4MzllZDIwNTQ4Y2M4YWE4Yzk3OGMxMDMwZTVm
//        Is latest: true
//        Is delete marker: false
//        key name: test2/text2.txt
//        versionid: CAEQBBiBgMC7u4v1kxciIGFlY2M1ZDY1OGE0NzQwYWE5NTVkMDg3YWQ0ODcwMWIz
//        Is latest: true
//        Is delete marker: false
//        key name: test3/lucene.avi
//        versionid: CAEQBBiBgICZ0K31kxciIDQyZGRhYzQ5ZGQwZjQzMWM4ZjJjNTRkZGIzM2FkNjlk
//        Is latest: true
//        Is delete marker: false
//        key name: 汽车灯标识.jpg
//        versionid: CAEQAhiBgIDLiq6bkBciIDA2NmVhNTVjYWQzNzQxMDM5ZmExYWIyNTQyN2Y0ZmY0
//        Is latest: false
//        Is delete marker: false
//        key name: 汽车灯标识.jpg
//        versionid: CAEQAhiBgMDY_q2bkBciIGQyOWFmNDcxYzZmNTQ1ZWQ5ZWMyMjdjYmZjMGZmZjU2
//        Is latest: false
//        Is delete marker: false
//        key name: 汽车灯标识.jpg
//        versionid: null
//        Is latest: false
//        Is delete marker: false
//        key name: fff/
//        versionid: CAEQBBiBgIDroMvkkxciIDIxNDQ2NmY2ZGQ3ZDRlNjg4M2RjMGE2YTU2ZTg0MTFm
//        Is latest: true
//        Is delete marker: true